#include <iostream>
#include <string>
#include <vector>
#include <chrono>

/**
 * intel SIMD Docs: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html
 */
#pragma GCC optimize(0)
using namespace std;
using namespace std::chrono;
#ifdef __SSE2__

#include <emmintrin.h>

#endif

template<char not_case_lower_bound, char not_case_upper_bound>
struct LowerUpperImpl {
public:
    static void arraySSE(const char *src, const char *src_end, char *dst) {

        const auto flip_case_mask = 'A' ^'a';
//#ifdef __SSE2__
        const auto bytes_sse = sizeof(__m128i);
        const auto src_end_sse = src_end - (src_end - src) % bytes_sse;
        const auto v_not_case_lower_bound = _mm_set1_epi8(not_case_lower_bound - 1);
        const auto v_not_case_upper_bound = _mm_set1_epi8(not_case_upper_bound + 1);
        const auto v_flip_case_mask = _mm_set1_epi8(flip_case_mask);


        for (; src < src_end_sse; src += bytes_sse, dst += bytes_sse) {
            const auto chars = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
            const auto is_not_case
                    = _mm_and_si128(_mm_cmpgt_epi8(chars, v_not_case_lower_bound),
                                    _mm_cmplt_epi8(chars, v_not_case_upper_bound));
            const auto xor_mask = _mm_and_si128(v_flip_case_mask, is_not_case);
            const auto cased_chars = _mm_xor_si128(chars, xor_mask);
            _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), cased_chars);
        }

//#endif

        for (; src < src_end; ++src, ++dst) {
            if (*src >= not_case_lower_bound && *src <= not_case_upper_bound)
                *dst = *src ^ flip_case_mask;
            else
                *dst = *src;

            // cout << "do loop";
        }


    }


    static void arrayNoSSE(const char *src, const char *src_end, char *dst) {
        const auto flip_case_mask = 'A' ^'a';
        for (; src < src_end; ++src, ++dst)
            if (*src >= not_case_lower_bound && *src <= not_case_upper_bound)
                *dst = *src ^ flip_case_mask;
            else
                *dst = *src;
    }


};


int main() {
    char src[261] = {'\0'};
    char dst[261] = {'\0'};
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 26; j++) {
            src[i * 26 + j] = 'a' + j;
        }
    }
    LowerUpperImpl<'a', 'z'> lowerUpper;
    auto start1 = system_clock::now();
    for (int i = 0; i < 1000; i++) {
        lowerUpper.arraySSE(&src[0], &src[261], &dst[0]);
    }


    auto end1 = system_clock::now();
   //cout << dst << endl;
    auto duration1 = duration_cast<nanoseconds>(end1 - start1);
    cout << "SSE Time cost: " << duration1.count() / 1000.0 << " ms" << endl;


    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 26; j++) {
            src[i * 26 + j] = 'b' + j;
        }
    }
    auto start2 = system_clock::now();
    for (int i = 0; i < 1000; i++) {
        lowerUpper.arrayNoSSE(&src[0], &src[261], &dst[0]);
    }
    auto end2 = system_clock::now();
    //cout << dst << endl;
    auto duration2 = duration_cast<nanoseconds>(end2 - start2);
    cout << "Time cost: " << duration2.count() / 1000.0 << " ms" << endl;

    /*
     * execute result may be :
     * SSE Time cost: 962.996 ms
     * Time cost: 2908.1 ms
     */


}